/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.veditor.controller;

import javax.swing.SwingConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.NavigationFilter;
import javax.swing.text.Position;
import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.model.DocumentContext;
import offset.nodes.client.veditor.model.DataModel;

public class DataNavigationFilter extends NavigationFilter {

    int direction = -1;
    Position.Bias[] biasRet = null;
    Editor editor = null;
    int oldPos = -1;
    int newPos = -1;
    boolean filter = true;

    public boolean isFilter() {
        return filter;
    }

    public void setFilter(boolean filter) {
        this.filter = filter;
    }

    public DataNavigationFilter(Editor editor) {
        this.editor = editor;
    }

    public int getNewPos() {
        return newPos;
    }

    public int getOldPos() {
        return oldPos;
    }

    protected boolean canPosition(int pos) {
        DataModel model = new DataModel(new DocumentContext(editor.getBrowserPane()), editor.getProperties());
        if (model.inTransparentElement(pos) || (model.hasData(pos) && model.canEdit(pos)))
            return true;
        return false;
    }

    protected int correctSelection(int pos, int newPos) {
        if (!filter)
            return newPos;
        if (newPos > pos) {
            for (int i = pos; i < newPos; i++) {
                if (!canPosition(i))
                    return i - 1;
            }
        } else
            for (int i = pos; i > newPos; i--) {
                if (!canPosition(i))
                    return i + 1;
            }
        return newPos;
    }

    protected int correctPosition(int dot, Position.Bias bias) {
        if (!filter)
            return dot;
        try {
            int originalDot = dot;
            while (!canPosition(dot)) {
                switch (direction) {
                    case SwingConstants.NORTH:
                    case SwingConstants.SOUTH:
                        int before = dot;
                        dot = super.getNextVisualPositionFrom(editor.getBrowserPane(), dot, bias, direction, biasRet);
                        if (dot == before)
                            return -1;
                        break;
                    case SwingConstants.WEST:
                        dot--;
                        if (dot < 0)
                            dot = editor.getBrowserPane().getDocument().getLength() - 1;
                        break;
                    case SwingConstants.EAST:
                        dot++;
                        if (dot >= editor.getBrowserPane().getDocument().getLength())
                            dot = 0;
                        break;
                    default:
                        if (bias == Position.Bias.Backward) {
                            dot--;
                            if (dot < 0)
                                dot = editor.getBrowserPane().getDocument().getLength() - 1;
                        }
                        else {
                            dot++;
                            if (dot >= editor.getBrowserPane().getDocument().getLength())
                                dot = 0;
                        }
                }
                if (dot == originalDot)
                    return dot; // reached original position again, prevent loop
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        return dot;
    }

    public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
        DataModel model = new DataModel(new DocumentContext(editor.getBrowserPane()), editor.getProperties());
        if (!model.isInTemplate(dot) || !filter)
            fb.setDot(dot, bias);
        oldPos = editor.getBrowserPane().getCaretPosition();
        newPos = correctPosition(dot, bias);
        if (newPos < 0) {
            newPos = oldPos;
            return;
        }
        fb.setDot(newPos, bias);
    }

    public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
        DataModel model = new DataModel(new DocumentContext(editor.getBrowserPane()), editor.getProperties());
        if (!model.isInTemplate(dot) || !filter)
            fb.moveDot(dot, bias);
        oldPos = editor.getBrowserPane().getCaretPosition();
        newPos = correctPosition(dot, bias);
        if (newPos < 0) {
            newPos = oldPos;
            return;
        }
        fb.moveDot(correctSelection(oldPos, newPos), bias);
    }

    public int getNextVisualPositionFrom(JTextComponent text, int pos, Position.Bias bias, int direction, Position.Bias[] biasRet) throws BadLocationException {
        this.direction = direction;
        this.biasRet = biasRet;
        return super.getNextVisualPositionFrom(text, pos, bias, direction, biasRet);
    }
}
